package wiiusej;

import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

import wiiusej.wiiusejevents.GenericEvent;
import wiiusej.wiiusejevents.utils.EventsGatherer;
import wiiusej.wiiusejevents.wiiuseapievents.StatusEvent;
import wiiusej.wiiusejevents.wiiuseapievents.WiiUseApiEvent;
import wiiusej.wiiusejevents.wiiuseapievents.WiimoteEvent;

public class MessageDeliver implements Runnable{

	
	//Wiimote API Listener
	private static WiiUseApiManager wiimoteManager;
	
	//Logger 
	private static Logger fileLogger;
	
	//Queue for getting the COM channels
	BlockingQueue<HashMap<Integer, BlockingQueue<GenericEvent>>> fromConnection;
	BlockingQueue<WiiUseApiEvent> toConnection;
	BlockingQueue<WiiUseApiEvent> toNetListener;
	
	//COM channels with wiimotes
	private static HashMap<Integer, BlockingQueue<GenericEvent>> wiimoteComChannels;
	
	private int connected = 0;
	private AtomicBoolean running = new AtomicBoolean(false);
	
	
	private int statusEventCounter = 0;
	private long genericPacketCounter = 0;
	private long statusPacketCounter = 0;
	
	
	
	public MessageDeliver(BlockingQueue<HashMap<Integer, BlockingQueue<GenericEvent>>> connectionToAPI, 
			BlockingQueue<WiiUseApiEvent> apiToConnection, WiiUseApiManager controller,
			BlockingQueue<WiiUseApiEvent> toNetListener){
		
		this.fromConnection = connectionToAPI;
		this.toConnection = toConnection;
		this.toNetListener = toNetListener;
		
		this.wiimoteManager = controller;
		
		//Logger init
		PropertyConfigurator.configure("log4j.properties");
		fileLogger = Logger.getLogger("messageDeliver");
		
		fileLogger.info("Init Message Deliver");
	}
	
	
	
	@Override
	public void run() {
		
		fileLogger.debug("Waiting for the barrier");
		
		SimpleDateFormat tsFormat = new SimpleDateFormat("mm:ss:SS");
		
		//listen the blocking queue from ConnectionManager
		try {
			wiimoteComChannels = fromConnection.take();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		fileLogger.debug("Received Message from ConnectionManger - Wiimotes: " 
				+ wiimoteComChannels.size() + " Time: " + tsFormat.format(System.currentTimeMillis()));	
		
		this.connected = wiimoteComChannels.size();
		
		fileLogger.debug("Found Connected Channels: " + wiimoteComChannels + 
				"Connected: " + this.connected);
		
		running.set(true);		
		
		EventsGatherer gather = new EventsGatherer(connected);	
			
		//Discard Events which have arrived before the wake-up process
		wiimoteManager.specialPoll(gather);
		gather.clearEvents();
		
		fileLogger.debug("");
		fileLogger.debug("*******************************");
		fileLogger.debug("EVENTS GATHERED BEFORE INIT APP: " + gather.getEvents().length);
		fileLogger.debug("*******************************");
		fileLogger.debug("");			
		
		//Request for Wiimote Status
		//wiimoteManager.getStatus(11);		
		
		while (running.get() && !wiimoteComChannels.isEmpty()) {
						
			//Check if there were changes in the wiimote connection
			
			HashMap<Integer, Wiimote> uploadedWiimotes;
			
			//EVENT POLLING
			wiimoteManager.specialPoll(gather);
						
			//deal with events gathered in Wiiuse API
			for (WiiUseApiEvent evt : gather.getEvents()) {
				
				statusPacketCounter++;
				fileLogger.info("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
				fileLogger.info("Event Received: " + evt.getEventType() 
						+ " From: " + evt.getWiimoteId() 
						+ " at: " + tsFormat.format(System.currentTimeMillis()) + 
						" #: " + statusPacketCounter);
				fileLogger.info("");			
				
				
				
				/*
				if (evt.getEventType() == WiiUseApiEvent.GENERIC_EVENT) {
					fileLogger.info("GENERIC_EVENT recevied");
					
					if((evt instanceof WiimoteEvent)){					
						fileLogger.info("WIIMOTE_EVENT instance");
						fileLogger.info("ButtonEvent: " + ((WiimoteEvent) evt).getButtonsEvent());								
						fileLogger.info(" MotionEvent" + ((WiimoteEvent) evt).getMotionSensingEvent());
						fileLogger.info(" IrEvent: " + ((WiimoteEvent) evt).getIREvent());	
							
								
					}		
					
				}*/				
				
				if (evt.getWiimoteId() != -1) {// event filled					
					
					switch(evt.getEventType()){
					
					case WiiUseApiEvent.DISCONNECTION_EVENT:
						
						fileLogger.info("");
						fileLogger.info("DISCONNECTION EVENT RECEIVED");
						fileLogger.info("");
						
						try {
							toConnection.put(evt);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						
						break;
						
					case WiiUseApiEvent.STATUS_EVENT:
						
						statusEventCounter++;										
						evt.setRefNumber(statusEventCounter);
						StatusEvent status = (StatusEvent) evt;
						
						
						//fileLogger.info(status);
						//fileLogger.info("*********** Sending Status info to NetListener: " + statusEventCounter);
						//fileLogger.info("");
						notifyNetListener(evt);
						break;
					
					default: 
						notifyWiiUseApiListener(evt);
						break;
					}					
					
				} else {
					fileLogger.error("NON-VALID WIIMOTE ID");
				}
			}
			gather.clearEvents();
		}		
		
		
	}

	
	
	private void notifyNetListener(WiiUseApiEvent evt) {
		
		try {				
			toNetListener.put(evt);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}	
		
	}



	/**
	 * Notify WiiUseApiListeners that an event occured.
	 * 
	 * @param evt
	 *            GenericEvent occured
	 */
	private void notifyWiiUseApiListener(WiiUseApiEvent evt) {
		
		String myName = Integer.toString(evt.getWiimoteId());
		
		if (evt.getEventType() == WiiUseApiEvent.GENERIC_EVENT) {
			
			HashMap<String, Timestamp> timeStamp;		
			Timestamp ts = new Timestamp (System.currentTimeMillis());
			timeStamp = ((WiimoteEvent) evt).getMotionSensingEvent().getTimeStamp();
			
			timeStamp = new HashMap<String, Timestamp>();
			timeStamp.put(myName,ts);
			
			((WiimoteEvent) evt).getMotionSensingEvent().setTimeStamp(timeStamp);
			((WiimoteEvent) evt).getMotionSensingEvent().setTime(ts);
			
			((WiimoteEvent) evt).setTime(ts);
			
			//set refNumber		
			genericPacketCounter++;
			evt.setRefNumber(genericPacketCounter);
			
			/*fileLogger.info("PRINTING EVENT: ");
			fileLogger.info(" " + (WiimoteEvent)evt);*/
			
		}
		
		BlockingQueue<GenericEvent> q = wiimoteComChannels.get(evt.getWiimoteId());
		
		if( q !=  null){
			//the data channel exists
			
			if((evt instanceof WiimoteEvent)){
				/*fileLogger.info("Packet: " + evt.getRefNumber() + 
						" Sending Event To: " + evt.getWiimoteId() +
						" Time: " + ((WiimoteEvent) evt).getTime());*/
				}
			
			try {				
				q.put(evt);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}			
			
		}else{
			fileLogger.info("ERROR finding comunucation channel. Wiimote: " + evt.getWiimoteId() + 
					" does NOT exists");
		}		
		
	}
	
	
	
	
}
